﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine.Material;
using PloobsEngine;
using PloobsEngine.Modelo;
using PloobsEngine.SceneControl;
using PloobsEngine.Cameras;

namespace Engine.Material.Deferred
{
    public enum DistorcionType
    {
        Perfect,MapDistorcion
    }

    public class PlanarReflection : IShader
    {
        
        private float specularIntensity = 0f;          
        private RenderTarget2D reflectionRT;
        private Texture2D reflectionMap;
        private Texture2D normal;
        private Effect _shader;          
        private float planeHeight;
        private int WIDTH;
        private int HEIGHT;
        private Plane plane;
        private VertexPositionTexture[] planeVertices;
        private Matrix reflectiveViewMatrix;
        private DistorcionType rtype = DistorcionType.MapDistorcion;
        private string distorcionMap = "Textures/normal";
        private float interpolatorColorFactor = 0.15f;

        public PlanarReflection(int width, int height, Plane plane, float Height)
        {
            this.WIDTH = width;
            this.HEIGHT = height;
            this.plane = plane;
            this.planeHeight = Height;
        }


        public float InterpolatorColorFactor
        {
            get { return interpolatorColorFactor; }
            set { interpolatorColorFactor = value; }
        }

        public DistorcionType DistorcionType
        {
            get { return rtype; }
            set { rtype = value; }
        }

        public string DistorcionMap
        {
            get { return distorcionMap; }
            set { distorcionMap = value; }
        }              
      
        public float SpecularIntensity
        {
            get { return specularIntensity; }
            set { specularIntensity = value; }
        }
        private float specularPower = 0f;

        public float SpecularPower
        {
            get { return specularPower; }
            set { specularPower = value; }
        }

        public override void PreDrawPhase(IWorld mundo, IModelo modelo, IRenderHelper render, ICamera cam)
        {            
                Matrix m =  Matrix.CreateReflection(plane) ;
                Vector3 pos;
                Vector3 target;
                Vector3 up;
                pos = Vector3.Transform(cam.Position, m);
                target = Vector3.Transform(cam.Target, m);
                up = Vector3.Transform(cam.Up, m);            
                reflectiveViewMatrix = Matrix.CreateLookAt(pos, target, up);
                
                Plane refractionClipPlane = CreatePlane(plane.D, plane.Normal, reflectiveViewMatrix,cam.Projection, false);

                EngineStuff.GraphicsDevice.ClipPlanes[0].Plane = refractionClipPlane;
                EngineStuff.GraphicsDevice.ClipPlanes[0].IsEnabled = true;
                EngineStuff.GraphicsDevice.SetRenderTarget(0, reflectionRT);
                EngineStuff.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

                //RenderScene   
                render.RenderSceneWithoutMaterial(mundo, new List<IObject>() { obj }, reflectiveViewMatrix, cam.Projection);            
                EngineStuff.GraphicsDevice.SetRenderTarget(0, null);
                reflectionMap = reflectionRT.GetTexture();
                EngineStuff.GraphicsDevice.ClipPlanes[0].IsEnabled = false;
        
        }               

        public override void Draw(IModelo modelo, IRenderHelper render, ICamera camera)
        {
            if (obj != null)
            {               
                if(rtype == DistorcionType.Perfect)
                   this._shader.CurrentTechnique = this._shader.Techniques["ReflexiveSurfacePerfect"];
                else if(rtype == DistorcionType.MapDistorcion)
                   this._shader.CurrentTechnique = this._shader.Techniques["ReflexiveSurface"];
             
                this._shader.Parameters["specularIntensity"].SetValue(specularIntensity);
                this._shader.Parameters["specularPower"].SetValue(specularPower);
                this._shader.Parameters["View"].SetValue(camera.View);
                this._shader.Parameters["interpolatorColorFactor"].SetValue(interpolatorColorFactor);                        
                this._shader.Parameters["ReflectionView"].SetValue(reflectiveViewMatrix);
                this._shader.Parameters["ReflectionMap"].SetValue(reflectionMap);
                this._shader.Parameters["FloorMap"].SetValue(modelo.getTexture(TextureType.DIFFUSE));
                this._shader.Parameters["normalMap"].SetValue(normal);
                this._shader.Parameters["id"].SetValue(shaderId);
                this._shader.Parameters["FarClip"].SetValue(camera.FarPlane);                      
                this._shader.Parameters["Projection"].SetValue(camera.Projection);


                Matrix[] boneTransforms = modelo.getBonesTransformation();
                Matrix wld = obj.getWorldMatrix();
                for (int i = 0; i < modelo.MeshNumber; i++)
                {
                    BatchInformation[] bi = modelo.GetBatchInformation(i);
                    Matrix w1 = Matrix.Multiply(wld, boneTransforms[modelo.GetParentBoneIndex(i)]);
                    this._shader.Parameters["World"].SetValue(w1);                        
                
                    for (int j = 0; j < bi.Count(); j++)
                    {

                        this._shader.Begin();
                        EngineStuff.GraphicsDevice.VertexDeclaration = bi[j].VertexDeclaration;
                        EngineStuff.GraphicsDevice.Vertices[0].SetSource(modelo.GetVertexBuffer(i), bi[j].StreamOffset, bi[j].VertexStride);
                        EngineStuff.GraphicsDevice.Indices = modelo.GetIndexBuffer(i);

                        this._shader.CurrentTechnique.Passes[0].Begin();
                        EngineStuff.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, bi[j].BaseVertex, 0, bi[j].NumVertices, bi[j].StartIndex, bi[j].PrimitiveCount);
                        this._shader.CurrentTechnique.Passes[0].End();
                        this._shader.End();
                    }

                }
            }
        }

        private Plane CreatePlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, Matrix camProj ,bool clipSide)
        {
            planeNormalDirection.Normalize();
            Vector4 planeCoeffs = new Vector4(planeNormalDirection, height);
            if (clipSide)
                planeCoeffs *= -1;

            Matrix worldViewProjection = currentViewMatrix * camProj;
            Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
            inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

            planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
            Plane finalPlane = new Plane(planeCoeffs);

            return finalPlane;
        }

        public override MaterialType MaterialType
        {
            get { return MaterialType.DEFERRED; }
        }

        public override void Initialize()
        {
            this._shader = EngineStuff.InternalContentManager.GetAsset<Effect>("Reflection");                                                       
            reflectionRT = EngineStuff.GetDefaultColorBuffer(WIDTH, HEIGHT);
            normal = EngineStuff.CustomContentManager.GetAsset<Texture2D>(distorcionMap);                                    
        }
    }
}
